goog.provide('dkpoints.rpc');

goog.require('goog.json');
goog.require('goog.net.XhrIo');
goog.require('goog.net.XhrManager');

goog.require('dkpoints.status');
goog.require('dkpoints.rpc.cache.Cache');
goog.require('dkpoints.rpc.commands');

/**
 * @type {goog.net.XhrManager}
 * @private
 */
dkpoints.rpc.xhrMgr = null;


/**
 * Don't instantiate the cache object until we get something worth caching.
 * @type {dkpoints.rpc.cache.Cache}
 * @private
 */
dkpoints.rpc.theCache = null;

/**
 * An object containing outstanding XHR requests.
 * Command objects are stored by commandID.
 * @type {Object} 
 * @private
 */
dkpoints.rpc.executedCommands = {};

/**
 * Process a command object passed from a content module.
 * @param {!dkpoints.rpc.commands.Command} command The command object to execute.
 */
dkpoints.rpc.execute = function(command) {
  var me = dkpoints.rpc;
  var xhr = null;
  
  if(me.xhrMgr === null) {
    xhr = me.xhrMgr = new goog.net.XhrManager();
  }
  else {
    xhr = me.xhrMgr;
  }
  
  /* If we're not forced to go to the server,
   * and the cache has been initialized,
   * then check if what we're looking for is in the cache. */


  // If anything is not cached, toggle this to false and send the XHR request.
  var gotEverything = true;
  if(! goog.isDef(command.forceRequest) && me.theCache !== null) {
    var cache = me.theCache;
    
    // Holder for stuff to fetch.
    var cachedData = null;
    
    // An array of cache keys.
    var dataToFetch = command.getCacheNames();
    cachedData = {};
    for(var i = 0, len = dataToFetch.length; ((i < len) && gotEverything); ++i) {
      var key = dataToFetch[i];
      
      var thisItem = cache.getCacheData(key);
      if( goog.isDef(thisItem) ) {
        cachedData[key] = thisItem;
      }
      else {
        gotEverything = false;
      }
    }
  }
  else {
    // Not allowed to cache, or cache was empty
    gotEverything = false;
  }
  
  if(! gotEverything) {
    // Not everything was in the cache, so set up and make the XHR call
    
    // Set up params.
    var retries = command.maxRetries;
    var callback = command.cache ? me.processCacheResponse : me.processResponse;
    
    // Make the call.
    var req = xhr.send(
      command.getCommandID(),
      command.url,
      "POST",
      "data=" + goog.json.serialize(command.jsonData),
      null,
      null,
      callback,
      retries ? null : retries
    );
    
    me.executedCommands[command.getCommandID()] = command;
  }
  else {
    command.handleFromCache(cachedData);
  }
};

/**
 * Process the response, ask the command object what should be cached, cache it,
 * then hand it back to the command's callback.
 * @param {goog.net.XhrManager.Event} event The event object from the XHR call's COMPLETE event.
 * @this {goog.net.XhrIo}
 */
dkpoints.rpc.processCacheResponse = function(event) {
  var me = dkpoints.rpc;
  var xhr = this; // The XhrIo instance, NOT dkpoints.rpc.
  var response = xhr.getResponseJson();
  
  var cmdID = response["commandID"];
  
  if(me.theCache === null) {
    me.theCache = new dkpoints.rpc.cache.Cache();
  }
  var cache = me.theCache;
  
  /**
   * @type {dkpoints.rpc.commands.Command}
   */
  var command = me.executedCommands[cmdID];
  delete me.executedCommands[cmdID]; // Remove command from the list.
  
  /*
   command.handleResponse will cherry-pick the data it needs from the XHR response,
   bundle that information in an object and hand it (and execution flow) back to the calling module.
   When that code finishes running, the command object will return to us an array of
   dkpoints.rpc.cache.CacheObjects.
  */
  var stuffToCache = command.handleResponse(response);
  for(var i = 0, len = stuffToCache.length; i < len; ++i) {
    var cacheItem = stuffToCache[i];
    cache.setCacheData(cacheItem);
  }
  
  cache.printToConsole();
};

/**
 * Process the response and hand it back to the command's callback.
 * @param {goog.net.XhrManager.Event} event The event object from the XHR call's COMPLETE event.
 * @this {goog.net.XhrIo}
 */
dkpoints.rpc.processResponse = function(event) {
  var me = this;
  
  console.log("No Cache Response.");
  console.log(me.getResponseJson());
};

/**
 * Tell the RPC service to clear the cache.
 */
dkpoints.rpc.clearCache = function() {
  dkpoints.rpc.theCache = null;
};

/**
 * Get the cache object.  This should only be called by the testing framework.
 * No other part of the app should deal directly with the cache.
 */
dkpoints.rpc.getCacheForTest = function() {
  var cache = dkpoints.rpc.theCache;
  if(goog.isDef(cache)) {
    return cache;
  }
  else {
    return null;
  }
};